using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

using System.IO;

namespace TraitorAmongUs {
	/// <summary>
	/// This is the main type for your game
	/// </summary>
	public class Engine : Microsoft.Xna.Framework.Game {
		public static bool LOG_PACKETS = false;

		public GraphicsDeviceManager graphics;
		public SpriteBatch spriteBatch;

		public StateManager stateManager;

		public Nuclex.Input.InputManager inputManager;
		public Nuclex.UserInterface.GuiManager guiManager;

		public Network.Server gameServer = null;   // Only used if hosting the game
		public Network.Client networkClient = null;

		bool changeCursorVisibility = false;
		bool changeCursorVisibility_Value;

		public Engine() {
			graphics = new GraphicsDeviceManager(this);
			graphics.IsFullScreen = false;
			graphics.PreferredBackBufferWidth = 1024;
			graphics.PreferredBackBufferHeight = 768;
			graphics.ApplyChanges();

			Content.RootDirectory = "data";

			inputManager = new Nuclex.Input.InputManager(Services, Window.Handle);
			Components.Add(inputManager);

			guiManager = new Nuclex.UserInterface.GuiManager(Services);

			Components.Add(guiManager);

			IsMouseVisible = true;

			stateManager = new StateManager(this);

			WriteLog("Engine Initialized");
		}

		/// <summary>
		/// Allows the game to perform any initialization it needs to before starting to run.
		/// This is where it can query for any required services and load any non-graphic
		/// related content.  Calling base.Initialize will enumerate through any components
		/// and initialize them as well.
		/// </summary>
		protected override void Initialize() {
			// TODO: Add your initialization logic here
			this.guiManager.Screen = new Nuclex.UserInterface.Screen(GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height);

			base.Initialize();

			stateManager.SetState(States.MainMenuState.Instance, null);
		}

		/// <summary>
		/// LoadContent will be called once per game and is the place to load
		/// all of your content.
		/// </summary>
		protected override void LoadContent() {
			// Create a new SpriteBatch, which can be used to draw textures.
			spriteBatch = new SpriteBatch(GraphicsDevice);

			// TODO: use this.Content to load your game content here
		}

		/// <summary>
		/// UnloadContent will be called once per game and is the place to unload
		/// all content.
		/// </summary>
		protected override void UnloadContent() {
			// TODO: Unload any non ContentManager content here
		}

		/// <summary>
		/// Allows the game to run logic such as updating the world,
		/// checking for collisions, gathering input, and playing audio.
		/// </summary>
		/// <param name="gameTime">Provides a snapshot of timing values.</param>
		protected override void Update(GameTime gameTime) {
			if(changeCursorVisibility) {
				changeCursorVisibility = false;
				this.IsMouseVisible = changeCursorVisibility_Value;
			}

			stateManager.Update(gameTime);

			base.Update(gameTime);
		}

		/// <summary>
		/// This is called when the game should draw itself.
		/// </summary>
		/// <param name="gameTime">Provides a snapshot of timing values.</param>
		protected override void Draw(GameTime gameTime) {
			GraphicsDevice.Clear(Color.CornflowerBlue);

			stateManager.Draw(gameTime);

			base.Draw(gameTime);
		}

		protected override void OnExiting(object sender, EventArgs args) {
			WriteLog("---");

			base.OnExiting(sender, args);

			stateManager.Exit();
		}


		/* There is a bug where if you change IsMouseVisible
		 * in another thread, it stops working.  We circumvent
		 * this by invoking it into this thread.
		 */
		public void SetCursorVisibility(bool visible) {
			changeCursorVisibility = true;
			changeCursorVisibility_Value = visible;
		}

		public static void WriteLog(string text, bool server = false) {
			try {
				string fileName = (server ? @"server_log.txt" : @"client_log.txt");
				using(StreamWriter logWriter = new StreamWriter(fileName, true)) {
					logWriter.WriteLine(text);
					logWriter.Close();
				}
			} catch(Exception) {
			}
		}
	}
}
